﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "RealmdSystem.h"
#include <csignal>
#include "Shared/Global_Macros.h"
#include "Shared/Base/LockGuard.h"
#include "Shared/Base/Thread.h"
#include "Shared/Log/Log.h"
#include "Shared/Util/Application.h"
#include "Realmd/Net/AuthSocketHandlerFactory.h"
using namespace Shared;
using namespace Shared::DB;

#define SOCKET_CHECK_INTERVAL   10000   /* 套接字管理 */
#define DB_PING_INTERVAL		60000
#define REALM_UPDATE_INTERVAL	60000


RealmdSystem::RealmdSystem(void)
    : SubSystem("Realmd")
    , _loginDB()
    , _loginDBPingTime(DB_PING_INTERVAL)
    , _NetServer(new AuthSocketHandlerFactory)

    , _realmList()
    , _realmListUpdateTime(REALM_UPDATE_INTERVAL)

    , _stopEvent()
    , _running(true)

    , _maxAuthTime(10000)
    , _unAuthedSockets()
    , _authedSockets()
    , _closingSockets()
    , _unAuthedSocketsLock()
    , _authedSocketsLock()
{

}

RealmdSystem::~RealmdSystem(void)
{

}

bool RealmdSystem::initialize(void)
{
    /* 初始化变量 */
    _maxAuthTime = sharedAppConfig.getInt("REALMD", "MaxAuthTime", 10000);

    /* 检查数据库 */
    if (!Shared::DB::Database::checkSanity()) {
        sharedLog.outError("Database::checkSanity() failed");
        return false;
    }

    /* 连接数据库 */
    const char * dbHost = sharedAppConfig.getString("REALMD", "DB_IP", "127.0.0.1");
    const char * dbPort = sharedAppConfig.getString("REALMD", "DB_Port", "3306");
    const char * dbUser = sharedAppConfig.getString("REALMD", "DB_User", "");
    const char * dbPass = sharedAppConfig.getString("REALMD", "DB_Pass", "");
    const char * dbName = sharedAppConfig.getString("REALMD", "DB_Name", "realmd");
    if (!_loginDB.initialize(dbHost, dbPort, dbUser, dbPass, dbName)) {
        sharedLog.outDbError("RealmdSystem::initialize() connect database"
            " with host:%s, port:%s, user:%s, pass:%s, db:%s failed, errror is '%s'\n",
            dbHost, dbPort, dbUser, dbPass, dbName, _loginDB.errorString().c_str());
        return false;
    }

    sharedLog.outDebug("[Login Database] -> (%s, %s, %s, %s, %s)",
        dbHost, dbPort, dbUser, dbPass, dbName);

    /* 启动网络服务器 */
    const char * host = sharedAppConfig.getString("REALMD", "NET_IP", "0.0.0.0");
    int port = sharedAppConfig.getInt("REALMD", "NET_Port", 3724);
    int backlog = sharedAppConfig.getInt("REALMD", "NET_Backlog", 1024);
    int workers = sharedAppConfig.getInt("REALMD", "NET_Threads", 0);
    if (!_NetServer.start(host, port, backlog, workers)) {
        sharedLog.outError("RealmdSystem::initialize() start TcpServer"
            " with host:%s, port:%d, backlog:%d, workers:%d failed, error is '%s'\n",
            host, port, backlog, workers, _NetServer.errorString().c_str());
        return false;
    }

    sharedLog.outDebug("[TcpServer] -> (%s, %d, %d, %d)",
        host, port, backlog, workers);

    return true;
}

void RealmdSystem::run(void)
{
    for(;;)
    {
        /* 管理套接字 */
        updateUnauthedSocket();
        updateAuthedSockets();

        /* Ping数据库 */
        _loginDBPingTime += SOCKET_CHECK_INTERVAL;
        if (_loginDBPingTime >= DB_PING_INTERVAL) {
            _loginDBPingTime = 0;
            _loginDB.ping();
            sharedLog.outDebug("RealmdSystem::run() ping database");
        }

        /* 更新服务器列表 */
        _realmListUpdateTime += SOCKET_CHECK_INTERVAL;
        if (_realmListUpdateTime >= REALM_UPDATE_INTERVAL) {
            _realmListUpdateTime = 0;
            _realmList.update();
        }

        _stopEvent.wait(SOCKET_CHECK_INTERVAL);
        if (!_running) {
            break;
        }
    }

    /* 关闭监听器 */
    _NetServer.closeAccepter();

    /* 关闭所有连接 */
    closeSockets();

    /* 关闭服务器 */
    _NetServer.stop();
}

void RealmdSystem::uninitialize(void)
{
    /* 关闭数据库 */
    _loginDB.close();
}

void RealmdSystem::onSignal(int signal)
{
    if (signal == SIGINT) {
        sharedLog.outError("RealmdSystem::onSignal() caught SIGINT, shutdown server");

        _running = false;
        _stopEvent.set();
    }
}

bool RealmdSystem::addUnauthedSocket(int id, AuthSocketHandler *sock)
{
    SHARED_LOCK_GURAD_ASSERT_RETURN_VAR(_unAuthedSocketsLock, false);
    _unAuthedSockets[id] = sock;
    return true;
}

bool RealmdSystem::moveSocketAuthed(int id)
{
    SHARED_LOCK_GURAD_ASSERT_RETURN_VAR(_unAuthedSocketsLock, false);
    SHARED_LOCK_GURAD_ASSERT_RETURN_VAR(_authedSocketsLock, false);

    AuthSocketHandler *movSock = NULL;
    {
        UnauthedSocketMap::iterator iter = _unAuthedSockets.find(id);
        if (iter != _unAuthedSockets.end()) {
            movSock = iter->second;
            _unAuthedSockets.erase(iter);
        }
    }

    SHARED_ASSERT(movSock);

    if (movSock)
    {
        std::uint32_t accid = movSock->accountId();
        AuthedSocketMap::iterator iter = _authedSockets.find(accid);
        if (iter != _authedSockets.end()) {
            /* 该帐号已经连接，将旧连接移动到关闭连接表，等待关闭 */
            if (!(iter->second->markedClose())) {
                sharedLog.outError("RealmdSystem::moveSocketAuthed() %d user repeat login ", 
                    iter->second->peer().id());
            }
            iter->second->markClose();
            _closingSockets[accid] = iter->second;
        }
        _authedSockets[accid] = movSock;
    }

    return true;
}

void RealmdSystem::updateUnauthedSocket(void)
{
    SHARED_LOCK_GURAD_ASSERT(_unAuthedSocketsLock);
    UnauthedSocketMap::iterator iter = _unAuthedSockets.begin();
    for (; iter != _unAuthedSockets.end(); ) {
        /* 检查死链接 */
        iter->second->checkDeadLink(_maxAuthTime);

        if ((iter->second->markedClose()) && (iter->second->tryClose())) {
            /* 连接已经关闭，将其移除 */
            iter = _unAuthedSockets.erase(iter);
            continue;
        }
        ++iter;
    }
}

void RealmdSystem::updateAuthedSockets(void) {
    SHARED_LOCK_GURAD_ASSERT(_authedSocketsLock);

    /* 关闭正常连接表中需要关闭的连接 */
    AuthedSocketMap::iterator iter = _authedSockets.begin();
    for (; iter != _authedSockets.end(); ) {
        if ((iter->second->markedClose()) && (iter->second->tryClose())) {
            /* 连接已经关闭，将其移除 */
            iter = _authedSockets.erase(iter);
            continue;
        }
        ++iter;
    }

    /* 关闭帐号被重复登录连接表中的连接 */
    iter = _closingSockets.begin();
    for (; iter != _closingSockets.end(); ) {
        if (iter->second->tryClose()) {
            /* 连接已经关闭，将其移除 */
            iter = _closingSockets.erase(iter);
            continue;
        }
        ++iter;
    }
}

void RealmdSystem::closeSockets(void)
{
    /* 关闭非验证连接 */
    {
        SHARED_LOCK_GURAD_ASSERT(_unAuthedSocketsLock);
        while (!_unAuthedSockets.empty()) {
            UnauthedSocketMap::iterator iter = _unAuthedSockets.begin();
            for (; iter != _unAuthedSockets.end(); ) {
                if (!iter->second->markedClose()) {
                    iter->second->markClose();
                }
                if ( iter->second->tryClose() ) {
                    iter = _unAuthedSockets.erase(iter);
                    continue;
                }
                ++iter;
            }

            Shared::Thread::sleep(100);
        }
    }

    /* 关闭验证连接 */
    {
        SHARED_LOCK_GURAD_ASSERT(_authedSocketsLock);
        while (!_authedSockets.empty()) {
            AuthedSocketMap::iterator iter = _authedSockets.begin();
            for (; iter != _authedSockets.end(); ) {
                if (!iter->second->markedClose()) {
                    iter->second->markClose();
                }
                if ( iter->second->tryClose() ) {
                    iter = _authedSockets.erase(iter);
                    continue;
                }
                ++iter;
            }

            Shared::Thread::sleep(100);
        }

        while (!_closingSockets.empty()) {
            AuthedSocketMap::iterator iter = _closingSockets.begin();
            for (; iter != _closingSockets.end(); ) {
                if (iter->second->tryClose()) {
                    iter = _closingSockets.erase(iter);
                    continue;
                }
                ++iter;
            }

            Shared::Thread::sleep(100);
        }
    }
}